/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package phd.dupenois.changeidentifiers.algorithms.chihull.display;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Container;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Stroke;
import java.awt.geom.Line2D;
import java.util.ArrayList;
import phd.dupenois.changeidentifiers.algorithms.chihull.DelaunayTriangulation;
import uk.co.dupenois.geometry.Circle;
import uk.co.dupenois.geometry.Coordinate;
import uk.co.dupenois.geometry.Line;
import uk.co.dupenois.grid.Grid;
import uk.co.dupenois.grid.GridListener;

/**
 *
 * @author mpd209
 */
public class DelaunayGrid extends Grid{

    private final static Stroke TRIANGLE_STROKE = new BasicStroke(2f);
    private final static Stroke REMOVE_EDGE_STROKE = new BasicStroke(4.0f ,
                                BasicStroke.CAP_ROUND,  
                                BasicStroke.JOIN_MITER, 
                                15.0f,                  
                                new float[] {10.0f,10.0f},
                                5.0f);
    private final static Stroke CANDIDATE_CHECK_STROKE = new BasicStroke(2f);
    private final static Color REMOVE_EDGE_COLOR = Color.RED;
    private final static Color MARKED_EDGE_COLOR = Color.BLACK;
    private final static Color LEFT_COLOUR = Color.BLUE;
    private final static Color RIGHT_COLOUR = Color.GREEN.darker();
    private final static Color MERGED_COLOUR = Color.MAGENTA.darker();
    private final static Color BASELINE_COLOUR = Color.ORANGE;
    private final static Color NEW_BASELINE_COLOUR = Color.RED;
    private final static Color CANDIDATE_LEFT_COLOUR = Color.BLUE;
    private final static Color CANDIDATE_RIGHT_COLOUR = Color.GREEN.darker();
    private final static Color POTENTIAL_CANDIDATE_COLOUR = Color.BLACK;
    private final static Color NEXT_CANDIDATE_COLOUR = Color.GRAY;
    private final static Color CANDIDATE_CHECK_COLOUR = Color.PINK;
    private final static Color PROPOSED_TRIANGLE_COLOUR = Color.PINK;
    private final static Color EXAMINED_COORDINATE_COLOUR = Color.ORANGE;



    private Line[] left;
    private Line[] right;
    private Line[] merged;
    private Line baseline;
    private Line newBaseline;
    private Line removeEdge;
    private Circle candidateCheck;
    private Coordinate candidateLeft;
    private Coordinate candidateRight;
    private Coordinate potentialCandidate;
    private Coordinate nextCandidate;
    private Coordinate examinedCoordinate;
    private ArrayList<Line> markedEdges;
    private Line[] proposedTriangle;

    private final static boolean RECORD = true;

    public DelaunayGrid(GridListener listener, Container container){
        super(listener, container, 5);
        COLOUR_GRID = Color.LIGHT_GRAY;
        this.showCoordinateLabels = true;
        this.markedEdges = new ArrayList<Line>();

    }

    public void drawLeftTriangulation(DelaunayTriangulation left){
        this.left = left.getEdgesLeftToRight().clone();
        this.merged = null;
        this.baseline = null;
        this.newBaseline = null;
        this.nextCandidate = null;
        this.candidateCheck = null;
        this.potentialCandidate = null;
        this.candidateLeft = null;
        this.candidateRight = null;
        this.removeEdge = null;
        this.proposedTriangle = null;
        this.examinedCoordinate = null;
        this.markedEdges = new ArrayList<Line>();
        this.repaint();
        if(RECORD)this.record();

    }

    public void drawRightTriangulation(DelaunayTriangulation right){
        this.right = right.getEdgesRightToLeft().clone();
        this.merged = null;
        this.baseline = null;
        this.newBaseline = null;
        this.nextCandidate = null;
        this.candidateCheck = null;
        this.potentialCandidate = null;
        this.candidateLeft = null;
        this.candidateRight = null;
        this.removeEdge = null;
        this.proposedTriangle = null;
        this.examinedCoordinate = null;
        this.markedEdges = new ArrayList<Line>();
        this.repaint();
        if(RECORD)this.record();
    }

    public void drawMergedTriangulation(DelaunayTriangulation merged){
        this.left = null;
        this.right = null;
        this.merged = merged.getEdgesLeftToRight().clone();
        this.baseline = null;
        this.newBaseline = null;
        this.nextCandidate = null;
        this.candidateCheck = null;
        this.potentialCandidate = null;
        this.candidateLeft = null;
        this.candidateRight = null;
        this.removeEdge = null;
        this.proposedTriangle = null;
        this.examinedCoordinate = null;
        this.markedEdges = new ArrayList<Line>();
        this.repaint();
        if(RECORD)this.record();
    }
    public void drawProposedTriangle(Line[] proposedTriangle){
        this.proposedTriangle = proposedTriangle.clone();
        this.repaint();
        if(RECORD)this.record();
    }
    public void drawCandidateCheck(Circle candidateCheck){
        this.merged = null;
        this.candidateCheck = candidateCheck.clone();
        this.repaint();
        if(RECORD)this.record();
    }

    public void drawCandidate(Coordinate candidate){
        this.nextCandidate = null;
        this.candidateCheck = null;
        this.examinedCoordinate = null;
        this.potentialCandidate = candidate.clone();
        this.repaint();
        if(RECORD)this.record();
    }
    public void drawExaminedCoordinate(Coordinate examinedCoordinate){
        this.examinedCoordinate = examinedCoordinate.clone();
        this.candidateCheck = null;
        this.repaint();
        if(RECORD)this.record();
    }
    public void drawNextCandidate(Coordinate nextCandidate){
        this.nextCandidate = nextCandidate.clone();
        this.candidateCheck = null;
        this.examinedCoordinate = null;
        this.repaint();
        if(RECORD)this.record();
    }

    public void markRemovalEdge(Line removeEdge){
        this.markedEdges.add(removeEdge);
        this.repaint();
        if(RECORD)this.record();
    }

    public void drawRemoveEdge(Line removeEdge){
        this.removeEdge = removeEdge.clone();
        this.markedEdges.remove(removeEdge);
        this.repaint();
        if(RECORD)this.record();
    }
    public void drawBaseline(Line baseline){
        this.baseline = baseline.clone();
        this.newBaseline = null;
        this.nextCandidate = null;
        this.candidateCheck = null;
        this.potentialCandidate = null;
        this.repaint();
        if(RECORD)this.record();
    }

    public void drawNewBaseline(Line newBaseline){
        this.newBaseline = newBaseline.clone();
        this.nextCandidate = null;
        this.candidateCheck = null;
        this.potentialCandidate = null;
        this.repaint();
    }

    public void drawCandidateLeft(Coordinate candidate){
        this.candidateLeft = candidate.clone();
        this.nextCandidate = null;
        this.candidateCheck = null;
        this.potentialCandidate = null;
        this.repaint();
        if(RECORD)this.record();
    }
    public void drawCandidateRight(Coordinate candidate){
        this.candidateRight = candidate.clone();
        this.nextCandidate = null;
        this.candidateCheck = null;
        this.potentialCandidate = null;
        this.repaint();
        if(RECORD)this.record();
    }

    


    @Override
    public void paint(Graphics g){
        super.paint(g);
        if(inPlayback()) return;
        Graphics2D g2 = (Graphics2D) g;
        Stroke originalStroke = g2.getStroke();
        drawEdges(g2, right, RIGHT_COLOUR, TRIANGLE_STROKE);
        drawEdges(g2, left, LEFT_COLOUR, TRIANGLE_STROKE);
        drawEdges(g2, merged, MERGED_COLOUR, TRIANGLE_STROKE);
        drawEdge(g2, baseline, BASELINE_COLOUR, TRIANGLE_STROKE);
        drawEdges(g2, proposedTriangle, PROPOSED_TRIANGLE_COLOUR, TRIANGLE_STROKE);
        drawEdge(g2, newBaseline, NEW_BASELINE_COLOUR, TRIANGLE_STROKE);
        drawEdges(g2, markedEdges.toArray(new Line[markedEdges.size()]), MARKED_EDGE_COLOR, REMOVE_EDGE_STROKE);
        drawEdge(g2, removeEdge, REMOVE_EDGE_COLOR, REMOVE_EDGE_STROKE);
        drawCoordinate(g2, candidateLeft, CANDIDATE_LEFT_COLOUR);
        drawCoordinate(g2, candidateRight, CANDIDATE_RIGHT_COLOUR);
        drawCoordinate(g2, potentialCandidate, POTENTIAL_CANDIDATE_COLOUR);
        drawCoordinate(g2, nextCandidate, NEXT_CANDIDATE_COLOUR);
        drawCoordinate(g2, examinedCoordinate, EXAMINED_COORDINATE_COLOUR);
        drawCircle(g2, candidateCheck, CANDIDATE_CHECK_COLOUR, CANDIDATE_CHECK_STROKE);
        g2.setStroke(originalStroke);
        

    }

    private void drawCoordinate(Graphics2D g2, Coordinate coordinate, Color colour){
        if(coordinate!=null){
            Coordinate clone = coordinate.clone();
            g2.setColor(colour);
            Point screenPos = getScreenPosition(clone);
            g2.fillOval(screenPos.x-5, screenPos.y-5, 10, 10);
        }
    }

    private void drawCircle(Graphics2D g2, Circle circle, Color colour, Stroke stroke){
        if(circle!=null){
            Circle clone = circle.clone();
            g2.setStroke(stroke);
            g2.setColor(colour);
            int radius = (int)Math.round(clone.getRadius());
//            Coordinate topLeft = new Coordinate(clone.getOrigin().getX()-radius, clone.getOrigin().getY()+radius);
            Point origin = getScreenPosition(clone.getOrigin());
            int radiusScaled = (int)Math.round(radius/getZoom());
            Point topLeft = new Point((int)Math.round(origin.x-radiusScaled),
                    (int)Math.round(origin.y-radiusScaled));

            g2.drawOval(topLeft.x, topLeft.y, radiusScaled*2, radiusScaled*2);
        }
    }

    private void drawEdge(Graphics2D g2, Line edge, Color colour, Stroke stroke){
        if(edge!=null){
            Line clone = edge.clone();
            g2.setStroke(stroke);
            g2.setColor(colour);
            Point screenStart = getScreenPosition(clone.getStart());
            Point screenFinish = getScreenPosition(clone.getFinish());
            g2.drawLine(screenStart.x, screenStart.y, screenFinish.x, screenFinish.y);
        }
    }
    private void drawEdges(Graphics2D g2, Line[] edges, Color colour, Stroke stroke){
        if(edges!=null && edges.length>0){
            Line[] clone = edges.clone();
            g2.setStroke(stroke);
            g2.setColor(colour);
            Point screenStart;
            Point screenFinish;
            for(Line line : clone ){
                screenStart = getScreenPosition(line.getStart());
                screenFinish = getScreenPosition(line.getFinish());
                g2.drawLine(screenStart.x, screenStart.y, screenFinish.x, screenFinish.y);
            }
        }
    }

}
